 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.views.navigator;

 import java.util.ArrayList ;
 import java.util.Iterator ;
 import java.util.List ;

 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IFolder;
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionProvider;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.swt.dnd.DND;
 import org.eclipse.swt.dnd.DragSource;
 import org.eclipse.swt.dnd.DragSourceAdapter;
 import org.eclipse.swt.dnd.DragSourceEvent;
 import org.eclipse.swt.dnd.DragSourceListener;
 import org.eclipse.swt.dnd.FileTransfer;
 import org.eclipse.swt.dnd.TransferData;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Shell;
 import org.eclipse.ui.actions.ReadOnlyStateChecker;
 import org.eclipse.ui.internal.views.navigator.ResourceNavigatorMessages;
 import org.eclipse.ui.part.ResourceTransfer;

 /**
  * Implements drag behaviour when items are dragged out of the
  * resource navigator.
  *
  * @since 2.0
  */
 public class NavigatorDragAdapter extends DragSourceAdapter {
     private static final String CHECK_MOVE_TITLE = ResourceNavigatorMessages.DragAdapter_title;

     private static final String CHECK_DELETE_MESSAGE = ResourceNavigatorMessages.DragAdapter_checkDeleteMessage;

     ISelectionProvider selectionProvider;

     private TransferData lastDataType;

     /**
      * Constructs a new drag adapter.
      * @param provider The selection provider
      */
     public NavigatorDragAdapter(ISelectionProvider provider) {
         selectionProvider = provider;
     }

     /**
      * This implementation of {@link DragSourceListener#dragFinished(DragSourceEvent)}
      * responds to a drag that has moved resources outside the Navigator by deleting
      * the corresponding source resource.
      */
     public void dragFinished(DragSourceEvent event) {
         LocalSelectionTransfer.getInstance().setSelection(null);

         if (event.doit == false) {
             return;
         }

         final int typeMask = IResource.FOLDER | IResource.FILE;
         if (event.detail == DND.DROP_MOVE) {
             //never delete resources when dragging outside Eclipse.
 //workaround for bug 30543.
 if (lastDataType != null
                     && FileTransfer.getInstance().isSupportedType(lastDataType)) {
                 return;
             }

             IResource[] resources = getSelectedResources(typeMask);
             DragSource dragSource = (DragSource) event.widget;
             Control control = dragSource.getControl();
             Shell shell = control.getShell();
             ReadOnlyStateChecker checker;

             if (resources == null || resources.length == 0) {
                 return;
             }

             checker = new ReadOnlyStateChecker(shell, CHECK_MOVE_TITLE,
                     CHECK_DELETE_MESSAGE);
             resources = checker.checkReadOnlyResources(resources);
             //delete the old elements
 for (int i = 0; i < resources.length; i++) {
                 try {
                     resources[i].delete(IResource.KEEP_HISTORY
                             | IResource.FORCE, null);
                 } catch (CoreException e) {
                     e.printStackTrace();
                 }
             }
         } else if (event.detail == DND.DROP_TARGET_MOVE) {
             IResource[] resources = getSelectedResources(typeMask);

             // file moved for us by OS, no need to delete the resources, just
 // update the view
 if (resources == null) {
                 return;
             }
             for (int i = 0; i < resources.length; i++) {
                 try {
                     resources[i].refreshLocal(IResource.DEPTH_INFINITE, null);
                 } catch (CoreException e) {
                     e.printStackTrace();
                 }
             }
         }
     }

     /**
      * This implementation of {@link DragSourceListener#dragSetData(DragSourceEvent)}
      * sets the drag event data based on the current selection in the Navigator.
      */
     public void dragSetData(DragSourceEvent event) {
         final int typeMask = IResource.FILE | IResource.FOLDER;
         IResource[] resources = getSelectedResources(typeMask);

         if (resources == null || resources.length == 0) {
             return;
         }

         lastDataType = event.dataType;
         //use local selection transfer if possible
 if (LocalSelectionTransfer.getInstance()
                 .isSupportedType(event.dataType)) {
             event.data = LocalSelectionTransfer.getInstance().getSelection();
             return;
         }
         //use resource transfer if possible
 if (ResourceTransfer.getInstance().isSupportedType(event.dataType)) {
             event.data = resources;
             return;
         }
         //resort to a file transfer
 if (!FileTransfer.getInstance().isSupportedType(event.dataType)) {
             return;
         }

         // Get the path of each file and set as the drag data
 final int length = resources.length;
         int actualLength = 0;
         String [] fileNames = new String [length];
         for (int i = 0; i < length; i++) {
             IPath location = resources[i].getLocation();
             // location may be null. See bug 29491.
 if (location != null) {
                 fileNames[actualLength++] = location.toOSString();
             }
         }
         if (actualLength == 0) {
             return;
         }
         // was one or more of the locations null?
 if (actualLength < length) {
             String [] tempFileNames = fileNames;
             fileNames = new String [actualLength];
             for (int i = 0; i < actualLength; i++) {
                 fileNames[i] = tempFileNames[i];
             }
         }
         event.data = fileNames;
     }

     /**
      * This implementation of {@link DragSourceListener#dragStart(DragSourceEvent)}
      * allows the drag to start if the current Navigator selection contains resources
      * that can be dragged.
      */
     public void dragStart(DragSourceEvent event) {
         lastDataType = null;
         // Workaround for 1GEUS9V
 DragSource dragSource = (DragSource) event.widget;
         Control control = dragSource.getControl();
         if (control != control.getDisplay().getFocusControl()) {
             event.doit = false;
             return;
         }

         IStructuredSelection selection = (IStructuredSelection) selectionProvider
                 .getSelection();
         for (Iterator i = selection.iterator(); i.hasNext();) {
             Object next = i.next();
             if (!(next instanceof IFile || next instanceof IFolder)) {
                 event.doit = false;
                 return;
             }
         }
         if (selection.isEmpty()) {
             event.doit = false;
             return;
         }
         LocalSelectionTransfer.getInstance().setSelection(selection);
         event.doit = true;
     }

     private IResource[] getSelectedResources(int resourceTypes) {
         List resources = new ArrayList ();
         IResource[] result = new IResource[0];

         ISelection selection = selectionProvider.getSelection();
         if (!(selection instanceof IStructuredSelection) || selection.isEmpty()) {
             return null;
         }
         IStructuredSelection structuredSelection = (IStructuredSelection) selection;

         // loop through list and look for matching items
 Iterator itr = structuredSelection.iterator();
         while (itr.hasNext()) {
             Object obj = itr.next();
             if (obj instanceof IResource) {
                 IResource res = (IResource) obj;
                 if ((res.getType() & resourceTypes) == res.getType()) {
                     resources.add(res);
                 }
             }
         }
         result = new IResource[resources.size()];
         resources.toArray(result);
         return result;
     }
 }

